import { extend } from 'shared/util'
import { CompilerOptions, CompiledResult, WarningMessage } from 'types/compiler'
import { detectErrors } from './error-detector'
import { createCompileToFunctionFn } from './to-function'
// Vue.js框架中用于创建编译器工厂函数的实现。Vue的模板编译器负责将模板字符串转换成可执行的渲染函数。
// 导出一个函数 createCompilerCreator，它接收一个编译函数 baseCompile 作为参数，并返回一个新的函数 createCompiler
export function createCompilerCreator(baseCompile: Function): Function {
  // createCompiler 函数接收一个编译选项对象 baseOptions 作为参数
  return function createCompiler(baseOptions: CompilerOptions) {
    // compile 函数是创建编译器的核心，它接收一个模板字符串 template 和一个可选的编译选项对象 options
    function compile(
      template: string,
      options?: CompilerOptions
    ): CompiledResult {
      // 创建一个新的对象 finalOptions，其原型为 baseOptions，用于存储最终的编译选项
      const finalOptions = Object.create(baseOptions)
      // 初始化一个空数组，用于存储编译过程中的错误信息
      const errors: WarningMessage[] = []
      // 初始化一个空数组，用于存储编译过程中的提示信息
      const tips: WarningMessage[] = []

      // 定义一个 warn 函数，用于在编译过程中记录警告信息或错误信息
      // 它接收一个消息 msg，一个位置范围 range，以及一个可选的提示信息 tip
      let warn = (
        msg: WarningMessage,
        range: { start: number; end: number },
        tip: string
      ) => {
        // 根据是否有 tip 参数，将警告信息推入到对应的数组（errors 或 tips）中
        ;(tip ? tips : errors).push(msg)
      }

      // 如果传入了 options 参数，则进行以下处理
      if (options) {
        // 在开发模式下，如果启用了输出源码范围的功能，则进行以下处理
        if (__DEV__ && options.outputSourceRange) {
          // $flow-disable-line 是一个 Flow 类型检查器的指令，用于禁用该行之后的类型检查
          // 计算模板字符串开头的空白字符数量
          const leadingSpaceLength = template.match(/^\s*/)![0].length

          // 重新定义 warn 函数，使其能够处理源码范围，并考虑开头的空白字符
          warn = (
            msg: WarningMessage | string,
            range: { start: number; end: number },
            tip: string
          ) => {
            // 将 msg 转换为 WarningMessage 对象（如果它还不是）
            const data: WarningMessage = typeof msg === 'string' ? { msg } : msg
            // 如果提供了 range，则调整 start 和 end 以考虑开头的空白字符
            if (range) {
              if (range.start != null) {
                data.start = range.start + leadingSpaceLength
              }
              if (range.end != null) {
                data.end = range.end + leadingSpaceLength
              }
            }
            // 根据是否有 tip 参数，将警告信息推入到对应的数组（errors 或 tips）中
            ;(tip ? tips : errors).push(data)
          }
        }
        // 如果提供了自定义模块，则将它们合并到 finalOptions.modules 中
        if (options.modules) {
          finalOptions.modules = (baseOptions.modules || []).concat(
            options.modules
          )
        }
        // 如果提供了自定义指令，则将它们合并到 finalOptions.directives 中
        if (options.directives) {
          finalOptions.directives = extend(
            Object.create(baseOptions.directives || null),
            options.directives
          )
        }
        // 复制其他选项到 finalOptions 中
        for (const key in options) {
          if (key !== 'modules' && key !== 'directives') {
            finalOptions[key] = options[key as keyof CompilerOptions]
          }
        }
      }

      // 将 warn 函数赋值给 finalOptions.warn
      finalOptions.warn = warn

      // 调用 baseCompile 函数进行编译，传入处理后的模板字符串和最终的编译选项
      const compiled = baseCompile(template.trim(), finalOptions)
      // 在开发模式下，检测编译后的 AST 中的错误
      if (__DEV__) {
        detectErrors(compiled.ast, warn)
      }
      // 将错误信息和提示信息赋值给编译结果对象
      compiled.errors = errors
      compiled.tips = tips
      // 返回编译结果
      return compiled
    }

    // 返回包含 compile 函数和 compileToFunctions 函数的对象
    // compileToFunctions 函数用于将模板编译成可执行的渲染函数
    return {
      compile,
      compileToFunctions: createCompileToFunctionFn(compile)
    }
  }
}
